/* compile with gcc async_io_test.c -laio -D_USE_GNU -o async_test 
   
   Author: Benedikt Niedermayr (2013)
 */


#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <libaio.h>
#include <getopt.h>
#include <string.h>
	
#define BUF_SIZE 	10
#define NUM_IOCBS	1024
#define IOCB_JOBS	2
char *buf[NUM_IOCBS];
char payload[1024];
char file[255];
int bytes = -1;

unsigned int hflag = 0, wflag = 0, rflag = 0, cnt = 1;
void parse_opts(int argc, char **argv)
{	
	int c;

	while((c = getopt(argc,argv,"hrw:c:d:b:")) != -1)
	{
		switch(c)
		{
			case 'h' : hflag = 1;                  				break;				/* help */
			case 'w' : wflag = 1; strcpy(&payload[0], optarg);	break; 
			case 'r' : rflag = 1; 								break;
			case 'c' : cnt = atoi(optarg);						break;
			case 'b' : bytes = atoi(optarg);					break;
			case 'd' : strcpy(file, optarg);					break; 
		}

	}
	
	if (hflag)
	{
		printf("Usage: %s [-w]/[-r] -c -d\n", argv[0]);		
		puts("-w		write");
		puts("-r		read");
		puts("-c 		how much asynchronous jobs should be started");
		puts("-b 		Number of bytes to be read/write");
		puts("-d		device file");
	exit(1);
	}

	if(bytes == -1) {
		puts("Please specify number of bytes to be read/write!");
		exit(1);
	}
}

int main( int argc, char **argv )
{
	io_context_t ctxp = NULL;
	struct iocb *iocbs[NUM_IOCBS];
	struct io_event events[NUM_IOCBS];
	struct timespec ts;

	int fd1, fd2, fd_sync;
	int result;
	int i,a;

	
	parse_opts(argc, argv);
	puts("[async-test] Starting async_io test!!");
	if(io_setup(NUM_IOCBS, &ctxp)) {
		perror("[async-test] io_setup");
		return -1;
	}
	fd1 = open(file, O_RDWR);


	if(fd1 < 0) { //|| fd2 < 0
		perror("[async-test] open");
		return -2;
	}
	
	for(i = 0; i < cnt; i++) {
		iocbs[i] = (struct iocb *)malloc(sizeof(struct iocb));
		posix_memalign((void**)&buf[i], 512, bytes + 1);
			
		if(rflag == 1){
			/* prepare a read job */
			printf("Prepare Read job. Read %i Bytes", bytes);
			io_prep_pread( iocbs[i], fd1, buf[i], bytes, 0 );
		} else if (wflag == 1) {
			strcpy(buf[i], payload);
			io_prep_pwrite(iocbs[i], fd1, buf[i], bytes, 0);		
		} else {
			puts("No read or write specified! Try again...");		
		}
		
		
	}
	i = cnt;

	/* start working prepared jobs.
	 */
	io_submit( ctxp, i, iocbs );
	
	/* you can also specify, how long io_getevents() is allowed to wait for 
     * completion of jobs
     */	
	ts.tv_sec = 60;
	ts.tv_nsec= 0;

	/* block while i jobs are ready and return 
	 * return_value to userspace
     */ 
	result = io_getevents(ctxp,(long)i, (long)i, events, NULL); //You can also use &ts instead of NULL
	
	for(a=0; a <= (i-1); a++) {

		/* Get result of each job */
		printf("[async-test] result= %d | result2= %d\n", events[a].res, events[a].res2);
		if(rflag)
			printf("[async-test] Payload from io-controller => %s\n", buf[a]);
		puts("");
		puts("");
	
	}	
	
	/* after all jobs are done, delete aio context */
	io_destroy(ctxp);
	return 0;
}
